﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Tester {
	class Program {

		static List<TestStructure>
			lHMACTestData = new List<TestStructure>(),
			lSHATestData = new List<TestStructure>();
		static ASCIIEncoding ascii = new ASCIIEncoding();

		static void Main(string[] args) {
			FillTestData();

			Profile_SHA3();
			ProfileHMACSHA3();

			PrintResult();
		}

		#region " Utility Methods "
		static string ToHexString(byte[] ba, bool wordBreak = false, int wordLength = 8) {
			string result = "";
			if (ba == null || ba.Length == 0) return "";
			char[] ca = new char[ba.Length * 2];
			byte b;
			for (int i = 0; i < ba.Length; ++i) {
				b = ((byte)(ba[i] >> 4));
				ca[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
				b = ((byte)(ba[i] & 0xF));
				ca[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
			}

			result = new string(ca);

			if (wordBreak) {
				for (int idx = 2 * wordLength; idx < result.Length; idx += 2 * wordLength + 1) {
					result = result.Insert(idx, " ");
				}
			}

			return result;
		}
		static byte[] ToByteArray(string hexString) {
			if (hexString.Length % 2 != 0) throw new NotSupportedException();
			byte[] result = new byte[hexString.Length / 2];
			for (int i = 0; i < result.Length; i++) {
				string sub = hexString.Substring(i * 2, 2);
				result[i] = byte.Parse(sub, NumberStyles.HexNumber);
			}
			return result;
		}
		static void FillTestData() {
			//SHA3
			lSHATestData.Add(new TestStructure() {
				TestCase = "1",
				Message = ascii.GetBytes("abc"),
				Instance = ToByteArray("18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96")
			});
			lSHATestData.Add(new TestStructure() {
				TestCase = "2",
				Message = ascii.GetBytes("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"),
				Instance = ToByteArray("6aa6d3669597df6d5a007b00d09c20795b5c4218234e1698a944757a488ecdc09965435d97ca32c3cfed7201ff30e070cd947f1fc12b9d9214c467d342bcba5d")
			});
			lSHATestData.Add(new TestStructure() {
				TestCase = "3",
				Message = ascii.GetBytes("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"),
				Instance = ToByteArray("ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682")
			});
			lSHATestData.Add(new TestStructure() {
				TestCase = "4",
				Message = ascii.GetBytes(new string('a', (int)(1e+6))),
				Instance = ToByteArray("5cf53f2e556be5a624425ede23d0e8b2c7814b4ba0e4e09cbbf3c2fac7056f61e048fc341262875ebc58a5183fea651447124370c1ebf4d6c89bc9a7731063bb")
			});
			lSHATestData.Add(new TestStructure() {
				TestCase = "5",
				Message = ascii.GetBytes(""),
				Instance = ToByteArray("0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e")
			});


			//HMAC-SHA3
			lHMACTestData.Add(new TestStructure() {
				TestCase = "1",
				Key = ToByteArray("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
				Message = ascii.GetBytes("Hi There"),
				Instance = ToByteArray("8852c63be8cfc21541a4ee5e5a9a852fc2f7a9adec2ff3a13718ab4ed81aaea0b87b7eb397323548e261a64e7fc75198f6663a11b22cd957f7c8ec858a1c7755")
			});
			lHMACTestData.Add(new TestStructure() {
				TestCase = "2",
				Key = ToByteArray("4a656665"),
				Message = ascii.GetBytes("what do ya want for nothing?"),
				Instance = ToByteArray("c2962e5bbe1238007852f79d814dbbecd4682e6f097d37a363587c03bfa2eb0859d8d9c701e04cececfd3dd7bfd438f20b8b648e01bf8c11d26824b96cebbdcb")
			});
			lHMACTestData.Add(new TestStructure() {
				TestCase = "3",
				Key = ToByteArray("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
				Message = ToByteArray("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"),
				Instance = ToByteArray("eb0ed9580e0ec11fc66cbb646b1be904eaff6da4556d9334f65ee4b2c85739157bae9027c51505e49d1bb81cfa55e6822db55262d5a252c088a29a5e95b84a66")
			});
			lHMACTestData.Add(new TestStructure() {
				TestCase = "4",
				Key = ToByteArray("0102030405060708090a0b0c0d0e0f10111213141516171819"),
				Message = ToByteArray("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"),
				Instance = ToByteArray("b46193bb59f4f696bf702597616da91e2a4558a593f4b015e69141ba81e1e50ea580834c2b87f87baa25a3a03bfc9bb389847f2dc820beae69d30c4bb75369cb")
			});
			lHMACTestData.Add(new TestStructure() {
				TestCase = "5",
				Key = ToByteArray("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"),
				Message = ToByteArray("546573742057697468205472756e636174696f6e"),
				Instance = ToByteArray("04c929fead434bba190dacfa554ce3f5")
			});
			lHMACTestData.Add(new TestStructure() {
				TestCase = "6",
				Key = ToByteArray("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
				Message = ToByteArray("54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b6579202d2048617368204b6579204669727374"),
				Instance = ToByteArray("d05888a6ebf8460423ea7bc85ea4ffda847b32df32291d2ce115fd187707325c7ce4f71880d91008084ce24a38795d20e6a28328a0f0712dc38253370da3ebb5")
			});
			lHMACTestData.Add(new TestStructure() {
				TestCase = "6a",
				Key = ToByteArray("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
				Message = ToByteArray("54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b6579202d2048617368204b6579204669727374"),
				Instance = ToByteArray("0f01da58d52a7e4ef3386bf7edb66625ff5c25385c3887d3ac9918c0828ba80c0db2de5bca3398f9694f7fd51535203a9e1f73ac4d9019383b5520bc26d2d654")
			});
			lHMACTestData.Add(new TestStructure() {
				TestCase = "7",
				Key = ToByteArray("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
				Message = ToByteArray("5468697320697320612074657374207573696e672061206c6172676572207468616e20626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520686173686564206265666f7265206265696e6720757365642062792074686520484d414320616c676f726974686d2e"),
				Instance = ToByteArray("2c6b9748d35c4c8db0b4407dd2ed2381f133bdbd1dfaa69e30051eb6badfcca64299b88ae05fdbd3dd3dd7fe627e42e39e48b0fe8c7f1e85f2dbd52c2d753572")
			});
			lHMACTestData.Add(new TestStructure() {
				TestCase = "7a",
				Key = ToByteArray("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
				Message = ToByteArray("5468697320697320612074657374207573696e672061206c6172676572207468616e20626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520686173686564206265666f7265206265696e6720757365642062792074686520484d414320616c676f726974686d2e"),
				Instance = ToByteArray("6adc502f14e27812402fc81a807b28bf8a53c87bea7a1df6256bf66f5de1a4cb741407ad15ab8abc136846057f881969fbb159c321c904bfb557b77afb7778c8")
			});
		}
		static void PrintResult() {
			Console.WriteLine(new string('=', 80));
			Console.WriteLine("SHA3 Tests".FormatWriteCenter(80));
			Console.WriteLine(new string('=', 80));
			for (int i = 0; i < lSHATestData.Count; i++) {
				Console.WriteLine("Test Case: {0}\tHash: {1}", lSHATestData[i].TestCase, ToHexString(lSHATestData[i].Hash, true, 8));
				if (lSHATestData[i].IsMatch()) { Console.ForegroundColor = ConsoleColor.Green; } else { Console.ForegroundColor = ConsoleColor.Red; }
				Console.WriteLine("Test Case: {0}\tInst: {1}", lSHATestData[i].TestCase, ToHexString(lSHATestData[i].Instance, true, 8));
				Console.ResetColor();
				Console.WriteLine();
			}

			Console.WriteLine();

			Console.WriteLine(new string('=', 80));
			Console.WriteLine("HMAC-SHA3 Tests".FormatWriteCenter(80));
			Console.WriteLine(new string('=', 80));
			for (int i = 0; i < lHMACTestData.Count; i++) {
				Console.WriteLine("Test Case: {0,-2}\tHash: {1}", lHMACTestData[i].TestCase, ToHexString(lHMACTestData[i].Hash));
				if (lHMACTestData[i].IsMatch()) { Console.ForegroundColor = ConsoleColor.Green; } else { Console.ForegroundColor = ConsoleColor.Red; }
				Console.WriteLine("Test Case: {0,-2}\tHash: {1}", lHMACTestData[i].TestCase, ToHexString(lHMACTestData[i].Instance));
				Console.ResetColor();
				Console.WriteLine();
			}
		}

		#region " Utility Classes "
		internal class TestStructure {
			public string TestCase { get; set; }
			public byte[] Key { get; set; }
			public byte[] Message { get; set; }
			public byte[] Hash { get; set; }
			public byte[] Instance { get; set; }

			public bool IsMatch() {
				bool result = true;
				if (Hash == null || Instance == null || Hash.Length != Instance.Length) {
					result = false;
				} else {
					for (int i = 0; i < Hash.Length; i++) {
						if (Hash[i] != Instance[i]) {
							result = false;
							break;
						}
					}
				}
				return result;
			}
		}
		#endregion

		#endregion

		private static void Profile_SHA3() {
			SHA3Managed sha = new SHA3Managed();
			for (int i = 0; i < lSHATestData.Count; i++) {
				lSHATestData[i].Hash = sha.ComputeHash(lSHATestData[i].Message);
			}
		}

		private static void ProfileHMACSHA3() {
			for (int i = 0; i < lHMACTestData.Count; i++) {
				HMACSHA3 hmac = new HMACSHA3(lHMACTestData[i].Key);
				lHMACTestData[i].Hash = hmac.ComputeHash(lHMACTestData[i].Message);
			}
		}

	}

	#region " Extension "
	internal static class Extensions {
		internal static string FormatWriteCenter(this string s, int len) {
			int l = s.Length;
			return string.Format("{0}{1}", new string(' ', (len - l) / 2), s);
		}
	}
	#endregion
}
